#lang racket


(require "../ast/main.rkt"
         "../base/main.rkt"
         "../proxies/main.rkt")

(provide load-tikz-methods
         begin-picture
         end-picture
         
         ; points
         point
         
         ; lines
         arc
         elliptical-arc
         ellipse
         line
         spline
         
         ; text
         text
         
         ; generic shapes
         circle
         rectangle)


(define-syntax with-tikz-file
  (syntax-rules ()
    ((_ body ...)
     (parameterize ((current-output-port tikz-file))
       body ...))))

(define-syntax with-displayln
  (syntax-rules ()
    ((_ body ...)
     (with-tikz-file (displayln body) ...))))

(define-syntax with-tikz-file*
  (syntax-rules ()
    ((_ body ...)
     (with-tikz-file (with-displayln body ...)))))


(define prec 3)

(define (precision n)
  (if (integer? n)
      n
      (let ((f (expt 10 3)))
        (/ (truncate (* (exact->inexact n) f)) f))))

(define (format-c c)
  (format "(~A,~A)" (precision (xyz-x c)) (precision (xyz-y c))))

(define (display-c c)
  (display (format-c c)))

(define (format-angle a)
  (precision (degrees<-radians a)))


(define tikz-file #f)

(define (load-tikz-methods)
  (unless tikz-file
    (set! tikz-file 
          (open-output-file "C:\\Users\\joe\\Documents\\My Dropbox\\ThesisBackup\\tikz\\tikz.tex"
                            #:mode 'text
                            #:exists 'truncate/replace))))

;(define (begin-picture)
;  (with-tikz-file*
;   "\\documentclass{article}"
;   "\\usepackage{tikz}"
;   "\\usetikzlibrary[topaths]"
;   ""
;   "\\begin{document}"
;   "\\begin{center}"
;   "\\begin{tikzpicture}[transform shape]"
;   ""))

(define (begin-picture)
  #f)

;(define (end-picture)
;  (with-tikz-file*
;   ""
;   "\\end{tikzpicture}"
;   "\\end{center}"
;   "\\end{document}")
;  (close-output-port tikz-file))

(define (end-picture)
  (close-output-port tikz-file))


; points

(define (point c)
  (begin0
    (id)
    (with-tikz-file* "\\pgfpoint{" (xyz-x c) "}{" (xyz-y c) "}")))


; lines

(define (arc angle r)
  (let ((c u0)
        (ab 0)
        (ae angle))
    (begin0
      (id)
      (with-tikz-file*
       (format
        "\\draw ~A arc (~A:~A:~A);"
        (format-c (add-pol-c c r ab))
        (format-angle ab)
        (format-angle ae)
        (precision r))))))

(define (elliptical-arc angle x-r y-r)
  (let ((c u0)
        (ab 0)
        (ae angle))
    (begin0
        (id)
      (with-tikz-file*
       (format
        "\\draw ~A arc (~A:~A:~A and ~A);"
        (format-c (add-pol-c c x-r ab))
        (format-angle ab)
        (format-angle ae)
        (precision x-r)
        (precision y-r))))))

(define (ellipse x-r y-r)
  (let ((c u0))
    (begin0
        (id)
      (with-tikz-file*
       (format "\\draw[shift={~A}] (0,0) ellipse (~A and ~A);" (format-c c) x-r y-r)))))
;  (tikz "[rotate=")
;  (tikz-number (graus<-radianos fi))
;  (tikz "]")

(define (line closed? cs)
  (begin0
    (id)
    (with-tikz-file
     (display "\\draw ")
     (display (string-join (map format-c cs) " -- "))
     (when closed? (display " -- cycle"))
     (displayln ";"))))

(define (spline closed? cs type)  
  (define (inner-tangent c1 c2 c3)
    (let ((d1 (mult-c (sub-c c3 c1) 0.1))
          (d3 (mult-c (sub-c c1 c3) 0.1)))
      (list (add-c c2 d3) (add-c c2 d1))))
  
  (define (first-tangent closed? cs)
    (if closed?
        (inner-tangent (last cs) (first cs) (second cs))
        (midcoord (first cs) (second cs))))
  
  (define (last-tangent closed? cs)
    (if closed?
        (inner-tangent (last (butlast cs)) (last cs) (first cs))
        (midcoord (last cs) (last (butlast cs)))))
  
  (define (inner-tangents cs)
    (let loop ((c (first cs)) (cs (rest cs)) (ts (list)))
      (if (empty? (rest cs))
          ts
          (loop (first cs) (rest cs) (cons (inner-tangent c (first cs) (second cs)) ts)))))
  
  (define (draw-bezier c1 t1 t2 c2)
    (with-tikz-file*
     (format "\\draw ~A .. controls ~A and ~A .. ~A;" (format-c c1) (format-c t1) (format-c t2) (format-c c2))))
  
  (define (draw-open-spline cs ts)
    (let loop ((c1 (first cs)) (c2 (second cs)) (t1 (first ts)) (t2 (second ts)) (cs (list-tail cs 2)) (ts (list-tail ts 2)))
      (draw-bezier c1 t1 t2 c2)
      (if (empty? ts)
          (void)
          (loop c2 (first cs) (first ts) (second ts) (rest cs) (list-tail ts 2)))))
  
  (define (open-spline cs)
    (let ((ts
           (flatten
            (append
             (list (first-tangent #f cs))
             (inner-tangents cs)
             (list (last-tangent #f cs))))))
      (draw-open-spline cs ts)))
  
  (match cs
    ((list c1 c2)
     (line closed? cs))
    (_
     (begin0
       (id)
       (match type
         ((auto-spline-type)
          (if closed?
              (error 'spline "Closed splines not implemented")
              (open-spline cs)))
         ((tangent-spline-type begin-tangent end-tangent)
          (error 'spline "Tangent splines not implemented")))))))


; text

(define (tikz-text txt p0 p1 h rot x-scale incl horiz-just vert-just)
  (let ((scale-x (* 3.7 h (if x-scale x-scale 1)))
        (scale-y (* 3.7 h)))
    (with-tikz-file
     (printf "\\draw [anchor=base west] ~A" (format-c p0))
     (printf "node[font=\\fontfamily{phv}\\selectfont,outer sep=0pt,inner sep=0pt")
     (unless (and (= horiz-just 0) (= vert-just 0))
       (printf ",minimum width=~A" (precision (abs (/ (- (xyz-x p1) (xyz-x p0)) scale-x))))
       (printf ",minimum height=~A" (precision (abs (/ (- (xyz-y p1) (xyz-y p0)) scale-y)))))
     (unless (= rot 0)
       (printf ",rotate=~A" (format-angle rot)))
     (unless (= incl 0)
       (printf ",xslant=~A" (precision (sin incl))))
     (printf ",xscale=~A,yscale=~A]{~A};" scale-x scale-y txt))))

(define (text t r s str)
  #f)


; generic shapes

(define (circle c type r)
  (match type
    ((line-primitive-type)
     (begin0
       (id)
       (with-tikz-file*
        (format "\\draw ~A circle (~A);" (format-c c) (precision r)))))
    ((surface-primitive-type)
     (begin0
       (id)
       (with-tikz-file*
        (format "\\path[fill=white,draw] ~A circle (~A);" (format-c c) (precision r)))))))

(define (rectangle type width height)
  (let ((c u0))
    (let ((c1 (add-xy-c c (- (/2 width)) (- (/2 height))))
          (c2 (add-xy-c c (/2 width) (/2 height))))
      (match type
        ((line-primitive-type)
         (begin0
           (id)
           (with-tikz-file*
            (format "\\draw ~A rectangle ~A;" (format-c c1) (format-c c2)))))
        ((surface-primitive-type)
         (begin0
           (id)
           (with-tikz-file*
            (format "\\path[fill=white,draw] ~A rectangle ~A;" (format-c c1) (format-c c2)))))))))